package skill

import (
	"time"

	"gddgame.cc/galaxy/common/dsl/game"
	"gddgame.cc/galaxy/utils/orm"
)

type SkillData struct {
	// skill不会修改，保持引用即可
	Skills   []*Skill
	SkillMap map[int]*Skill
	// buff不会修改，保持引用即可
	Buffs   []*Buff
	BuffMap map[int]*Buff

	effectModel *orm.Model
	buffModel   *orm.Model
	skillModel  *orm.Model
}

func GetSkillData(env *game.Environment) (*SkillData, error) {
	var err error
	data := &SkillData{}

	skillProvider := &game.DataProvider{
		Name:  "skill",
		Array: true,
		Get: func() (i interface{}, err error) {
			return data.GetAllSkill(nil)
		},
	}
	env.RegisterData(skillProvider)
	buffProvider := &game.DataProvider{
		Name:  "buff",
		Array: true,
		Get: func() (i interface{}, err error) {
			return data.GetAllBuff(nil)
		},
	}
	env.RegisterData(buffProvider)

	if data.effectModel, _, err = getEffectData(env); err != nil {
		return nil, err
	}
	if data.skillModel, _, data.Skills, err = getSkillData(env, skillProvider); err != nil {
		return nil, err
	}
	if data.buffModel, _, data.Buffs, err = getBuffData(env, buffProvider); err != nil {
		return nil, err
	}

	data.SkillMap = make(map[int]*Skill, len(data.Skills))
	for index := range data.Skills {
		skill := data.Skills[index]
		data.SkillMap[skill.Id] = skill
	}
	data.BuffMap = make(map[int]*Buff, len(data.Buffs))
	for index := range data.Buffs {
		buff := data.Buffs[index]
		data.BuffMap[buff.Id] = buff
	}
	return data, nil
}

func getEffectData(env *game.Environment) (*orm.Model, []SettingEffect, error) {
	var err error
	model, err := env.GetModel(SettingEffectName)
	if err != nil {
		return nil, nil, err
	}
	var effects []SettingEffect
	_, err = model.Query().Find(&effects)
	if err != nil {
		return nil, nil, err
	}
	return model, effects, nil
}

func getSkillData(env *game.Environment, provider *game.DataProvider) (*orm.Model, []SettingSkill, []*Skill, error) {
	var err error

	model, err := env.GetModel(SettingSkillName)
	if err != nil {
		return nil, nil, nil, err
	}
	var skills []SettingSkill
	_, err = model.Query().Find(&skills)
	if err != nil {
		return nil, nil, nil, err
	}
	Skills := make([]*Skill, len(skills))
	for k := range skills {
		Skills[k] = NewSkill(provider, &skills[k])
	}
	return model, skills, Skills, nil
}

func getBuffData(env *game.Environment, provider *game.DataProvider) (*orm.Model, []SettingBuff, []*Buff, error) {
	var err error
	model, err := env.GetModel(SettingBuffName)
	if err != nil {
		return nil, nil, nil, err
	}
	var buffs []SettingBuff
	_, err = model.Query().Find(&buffs)
	if err != nil {
		return nil, nil, nil, err
	}
	Buffs := make([]*Buff, len(buffs))
	for k := range buffs {
		Buffs[k] = NewBuff(provider, &buffs[k])
	}
	return model, buffs, Buffs, nil
}

func (skillData *SkillData) GetAllSkill(updateTime *time.Time) ([]*Skill, error) {
	if updateTime != nil {
		skills := make([]*Skill, 0, 5)
		for _, skill := range skillData.Skills {
			if skill.HasUpdate(updateTime) {
				skills = append(skills, skill)
			}
		}
		return skills, nil
	} else {
		return skillData.Skills, nil
	}
}

func (skillData *SkillData) GetAllBuff(updateTime *time.Time) ([]*Buff, error) {
	if updateTime != nil {
		buffs := make([]*Buff, 0, 5)
		for _, buff := range skillData.Buffs {
			if buff.HasUpdate(updateTime) {
				buffs = append(buffs, buff)
			}
		}
		return buffs, nil
	} else {
		return skillData.Buffs, nil
	}
}

func (skillData *SkillData) GroupDistinctBuff(buffIds []int) map[int]*Buff {
	m := map[int]*Buff{}
	l := map[int]*Buff{}
	for _, id := range buffIds {
		buff := skillData.BuffMap[id]
		if buff.Define == 0 {
			l[buff.Id] = buff
			continue
		}
		if g, ok := m[buff.Define]; !ok {
			m[buff.Define] = buff
		} else if g.DefineLevel < buff.DefineLevel {
			m[buff.Define] = buff
		}
	}
	for _, buff := range m {
		l[buff.Id] = buff
	}
	return l
}

func (skillData *SkillData) DistinctBuff(buffIds []int) []int {
	m := map[int]*Buff{}
	l := []int{}
	for _, id := range buffIds {
		buff := skillData.BuffMap[id]
		if buff.Define == 0 {
			l = append(l, buff.Id)
			continue
		}
		if g, ok := m[buff.Define]; !ok {
			m[buff.Define] = buff
		} else if g.DefineLevel < buff.DefineLevel {
			m[buff.Define] = buff
		}
	}
	for _, buff := range m {
		l = append(l, buff.Id)
	}
	return l
}
